/*
 * Cache.java
 *
 * Created on January 3, 2008, 7:34 PM
 *
 * Copyright (C) 2007-2008  Hylke van der Schaaf
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, in version 3 of the License.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package hextd.util;

import com.kitfox.svg.SVGCache;
import com.kitfox.svg.SVGDiagram;
import com.kitfox.svg.SVGException;
import com.kitfox.svg.SVGUniverse;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.text.html.StyleSheet;

/**
 *
 * @author hylke
 */
public class Cache {

	private static Cache instance;
	private static final Map<Object, Cache> instances = new HashMap<Object, Cache>();
	//
	private HashMap<String, BufferedImage[]> cBufImgArr;
	private HashMap<String, Shape[]> cShapeArr;
	private HashMap<String, Shape> cShape;
	private SVGUniverse svgUniverse;
	private HashMap<String, URI> svgImages;
	private HashMap<String, StyleSheet> styleSheets;
	private HashMap<String, BufferedImage> images;
	private URL contextUrl;

	/** Creates a new instance of Cache */
	private Cache() {
		this.cBufImgArr = new HashMap<String, BufferedImage[]>();
		this.cShapeArr = new HashMap<String, Shape[]>();
		this.cShape = new HashMap<String, Shape>();
	}

	public static Cache getInstance() {
		if (instance == null) {
			instance = new Cache();
			instances.put(null, instance);
		}
		return instance;
	}

	public static Cache getInstance(Object key) {
		Cache cache = instances.get(key);
		if (cache == null) {
			cache = new Cache();
			instances.put(key, cache);
		}
		return cache;
	}

	public void clearCache() {
		this.cBufImgArr.clear();
		this.cShapeArr.clear();
		this.cShape.clear();
	}

	/**
	 * The url relative to which all relative urls will be resolved.
	 * This has to be set before any url resolution can be done.
	 *
	 * @return the base url.
	 */
	public URL getContextUrl() {
		return contextUrl;
	}

	/**
	 * The url relative to which all relative urls will be resolved.
	 * This has to be set before any url resolution can be done.
	 *
	 * @param contextUrl
	 */
	public void setContextUrl(URL contextUrl) {
		this.contextUrl = contextUrl;
	}

	/*
	 * Buffered Image Cache
	 */
	public boolean hasBufImgArr(String name) {
		return this.cBufImgArr.containsKey(name);
	}

	public void putBufImgArr(String name, BufferedImage[] a) {
		this.cBufImgArr.put(name, a);
	}

	public BufferedImage[] getBufImgArr(String name) {
		return this.cBufImgArr.get(name);
	}

	/*
	 * Shape Cache
	 */
	public boolean hasShapeArr(String name) {
		return this.cShapeArr.containsKey(name);
	}

	public void putShapeArr(String name, Shape[] a) {
		this.cShapeArr.put(name, a);
	}

	public Shape[] getShapeArr(String name) {
		return this.cShapeArr.get(name);
	}

	public boolean hasShape(String name) {
		return this.cShape.containsKey(name);
	}

	public void putShape(String name, Shape a) {
		this.cShape.put(name, a);
	}

	public Shape getShape(String name) {
		return this.cShape.get(name);
	}

	/*
	 * Stylesheet Cache
	 */
	public StyleSheet loadStyleSheet(String name) {
		if (styleSheets.containsKey(name)) {
			return this.styleSheets.get(name);
		} else {
			StyleSheet s = new StyleSheet();
			URL u;
			try {
				u = new URL(this.contextUrl, name);
				s.importStyleSheet(u);
			} catch (MalformedURLException ex) {
				Logger.getLogger(Cache.class.getName()).log(Level.SEVERE, null, ex);
			}

			this.styleSheets.put(name, s);
			return s;
		}

	}

	public void clearImages() {
		this.images.clear();
	}

	public BufferedImage loadImage(String location) {
		if (this.images.containsKey(location)) {
			return this.images.get(location);
		} else {
			BufferedImage i = null;
			URL u;
			try {
				u = new URL(this.contextUrl, location);
				i = ImageIO.read(u);
			} catch (MalformedURLException ex) {
				Logger.getLogger(Cache.class.getName()).log(Level.SEVERE, null, ex);
			} catch (IOException ex) {
				Logger.getLogger(Cache.class.getName()).log(Level.SEVERE, null, ex);
			}

			if (i == null) {
				i = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
			}

			this.images.put(location, i);
			return i;
		}

	}

	/*
	 * SVG Cache
	 */
	public URI loadSvg(String name, String location) {
		if (this.svgUniverse == null) {
			this.svgUniverse = SVGCache.getSVGUniverse();
		}

		if (!this.svgImages.containsKey(name)) {
			URL url;
			try {
				url = new URL(this.contextUrl, location);
			} catch (MalformedURLException ex) {
				Logger.getLogger(Cache.class.getName()).log(Level.SEVERE, null, ex);
				return null;
			}

			URI uri = this.svgUniverse.loadSVG(url);
			this.svgImages.put(name, uri);
		}

		return this.svgImages.get(name);
	}

	public void renderSvgImage(String name, Graphics2D g, int w, int h) {
		AffineTransform oldXform = g.getTransform();
		URI image = this.svgImages.get(name);
		SVGDiagram diagram = svgUniverse.getDiagram(image);
		Rectangle2D.Double rect = new Rectangle2D.Double();
		diagram.getViewRect(rect);

		AffineTransform scaleXform = new AffineTransform();
		//scaleXform.setToTranslation(50,50);
		scaleXform.setToScale(w / rect.width, h / rect.height);
		g.transform(scaleXform);

		//diagram.setDeviceViewport(new Rectangle(0, 0, w, h));
		diagram.setIgnoringClipHeuristic(true);
		try {
			diagram.render(g);
		} catch (SVGException ex) {
			Logger.getLogger(Cache.class.getName()).log(Level.SEVERE, null, ex);
		}

		g.setTransform(oldXform);
	}
}
